package edu.uts.reekie.designer.edit;

import java.awt.List;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;

import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractConnectionEditPart;
import org.eclipse.gef.editpolicies.ConnectionEditPolicy;
import org.eclipse.gef.editpolicies.ConnectionEndpointEditPolicy;
import org.eclipse.gef.requests.GroupRequest;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;

import edu.uts.reekie.designer.figures.ConnectorLabel;
import edu.uts.reekie.designer.figures.Fonts;
import edu.uts.reekie.designer.model.*;

/**
 * Edit part for Connector model elements.
 * <p>
 * This edit part must implement the PropertyChangeListener interface, so it can
 * be notified of property changes in the corresponding model element.
 * </p>
 * 
 * @author Vijay Santhanam
 */
public class AbstractConnectorEditPart extends AbstractConnectionEditPart
		implements PropertyChangeListener {

	public void activateFigure() {
		super.activateFigure();
		/*
		 * Once the figure has been added to the ConnectionLayer, start
		 * listening for its router to change.
		 */
		getFigure().addPropertyChangeListener(
				Connection.PROPERTY_CONNECTION_ROUTER, this);
	}

	public void deactivateFigure() {
		getFigure().removePropertyChangeListener(
				Connection.PROPERTY_CONNECTION_ROUTER, this);
		super.deactivateFigure();
	}

	/**
	 * Upon activation, attach to the model element as a property change
	 * listener.
	 */
	public void activate() {
		if (!isActive()) {
			super.activate();
			getConnectorModel().addPropertyChangeListener(this);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
	 */
	protected void createEditPolicies() {

		// Selection handle edit policy.
		// Makes the connection show a feedback, when selected by the user.
		installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE,
				new AbstractConnectorEndpointEditPolicy());
		// Allows the removal of the connection model element
		installEditPolicy(EditPolicy.CONNECTION_ROLE,
				new AbstractConnectorEditPolicy());

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
	 */
	protected IFigure createFigure() {
		PolylineConnection connection = (PolylineConnection) super
				.createFigure();
		
		

		applyCentreLabel(connection, getConnectorModel().getLabel());
		applySourceTargetLabel(connection, getConnectorModel().getSourceToTargetLabel());
		applyTargetSourceLabel(connection, getConnectorModel().getTargetToSourceLabel());
		
		applyDirectionDecorations(connection, getConnectorModel()
				.getDirection());
		connection.setLineStyle(Graphics.LINE_SOLID); // always a solid line

		return connection;
	}

	/**
	 * Upon deactivation, detach from the model element as a property change
	 * listener.
	 */
	public void deactivate() {
		if (isActive()) {
			super.deactivate();
			getConnectorModel().removePropertyChangeListener(this);
		}
	}

	private AbstractConnector getConnectorModel() {
		return (AbstractConnector) getModel();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seejava.beans.PropertyChangeListener#propertyChange(java.beans.
	 * PropertyChangeEvent)
	 */
	public void propertyChange(PropertyChangeEvent event) {
		// handle changes in model properties

		String property = event.getPropertyName();

		PolylineConnection c = (PolylineConnection) getFigure();
		if (AbstractConnector.PROPERTY_DIRECTION.equals(property)) {
			applyDirectionDecorations(c, getConnectorModel().getDirection());
			refreshVisuals();
		}

		if (AbstractConnector.PROPERTY_LABEL.equals(property)) {

			applyCentreLabel(c, getConnectorModel().getLabel());
			refreshVisuals();
		}

		if (AbstractConnector.PROPERTY_SOURCE_TO_TARGET_LABEL.equals(property)) {

			applySourceTargetLabel(c, getConnectorModel().getSourceToTargetLabel());
			
			refreshVisuals();
		}

		if (AbstractConnector.PROPERTY_TARGET_TO_SOURCE_LABEL.equals(property)) {

			applyTargetSourceLabel(c, getConnectorModel().getTargetToSourceLabel());
			refreshVisuals();
		}
		
		
	}

	class OffsetMidpointLocator extends MidpointLocator {

		int offset = 40;

		public OffsetMidpointLocator(Connection c, int i, int offset) {
			super(c, i);
			this.offset = offset;
		}

		protected Point getReferencePoint() {

			Point mid = super.getReferencePoint();
			Connection conn = getConnection();
			//Point p = Point.SINGLETON;
			Point p1 = conn.getPoints().getPoint(getIndex());
			Point p2 = conn.getPoints().getPoint(getIndex() + 1);
			conn.translateToAbsolute(p1);
			conn.translateToAbsolute(p2);

			double m = (p2.y - p1.y) / (p2.x - p1.x);
			if (m == 0) {
				return mid.translate(4, offset);
			}

			double nm = -1 * (1 / m);
			double dx = offset * 1;
			double dy = offset * nm;
			return mid.translate((int) Math.round(dx), (int) Math.round(dy));
		}

	}
	
	void applyCentreLabel(PolylineConnection c, String label) {
		applyLabel(c,label,ConnectorLabel.DIRECTION_NONE,new MidpointLocator(c,0));
	}
	
	void applySourceTargetLabel(PolylineConnection c, String label) {
		applyLabel(c,label,ConnectorLabel.DIRECTION_SOURCE_TO_TARGET,new OffsetMidpointLocator(c, 0, 15));
	}
	
	void applyTargetSourceLabel(PolylineConnection c, String label) {
		applyLabel(c,label,ConnectorLabel.DIRECTION_TARGET_TO_SOURCE,new OffsetMidpointLocator(c, 0, -15));
	}

	void applyLabel(PolylineConnection c, String label, int direction,
			Locator locator) {

		// remove the old figure for that same direction
		java.util.List<?> children = c.getChildren();
		ConnectorLabel oldLabel = null;
		for (int i = 0; i < children.size(); i++) {
			Object child = children.get(i);
			if (child instanceof ConnectorLabel) {
				if (((ConnectorLabel) child).getDirection() == direction) {
					oldLabel = (ConnectorLabel) child;
				}

			}
		}
		if (oldLabel != null)
			c.remove(oldLabel);

		// if the label is blank, don't add a new label for that direction and
		// locator
		if (label == null)
			return;

		// create the label
		ConnectorLabel l = new ConnectorLabel();
		l.setDirection(direction);
		l.setBackgroundColor(ColorConstants.gray);
		l.setText(label);
		l.setFont(Fonts.connector);

		TextUtilities tu = new TextUtilities();

		Dimension size = tu.getTextExtents(label, l.getFont());
		c.add(l);
		l.setSize(size);
		c.setConstraint(l, locator);

	}

	protected void applyDirectionDecorations(PolylineConnection c, int direction) {

		c.setTargetDecoration(new PolygonDecoration()); // arrow at target end
		// pt
		if (direction == AbstractConnector.BIDIRECTIONAL_CONNECTION) {
			c.setSourceDecoration(new PolygonDecoration()); // arrow at source
			// end point
		} else {
			c.setSourceDecoration(null); // arrow at source end point
		}
	}

}
